extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
}

#include <iostream>

void print_rtp_statistics(AVFormatContext* format_context, int video_stream_index) {
    AVDictionaryEntry *entry = nullptr;
    char buffer[1024];

    while ((entry = av_dict_get(format_context->streams[video_stream_index]->metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
        if (strcmp(entry->key, "RTP-NTP-TIME") == 0) {
            std::cout << "RTP NTP time: " << entry->value << std::endl;
        }
    }
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <rtsp_url>" << std::endl;
        return -1;
    }

    const char* rtsp_url = argv[1];

    // Initialize FFmpeg
    avformat_network_init();

    AVFormatContext* format_context = avformat_alloc_context();
    if (!format_context) {
        std::cerr << "Could not allocate format context" << std::endl;
        return -1;
    }

    if (avformat_open_input(&format_context, rtsp_url, nullptr, nullptr) != 0) {
        std::cerr << "Could not open input URL: " << rtsp_url << std::endl;
        avformat_free_context(format_context);
        return -1;
    }

    if (avformat_find_stream_info(format_context, nullptr) < 0) {
        std::cerr << "Could not find stream information" << std::endl;
        avformat_close_input(&format_context);
        return -1;
    }

    AVCodec* codec = nullptr;
    AVCodecParameters* codec_parameters = nullptr;
    int video_stream_index = -1;

    for (unsigned int i = 0; i < format_context->nb_streams; i++) {
        AVCodecParameters* local_codec_parameters = format_context->streams[i]->codecpar;
        const AVCodec* local_codec = avcodec_find_decoder(local_codec_parameters->codec_id);

        if (local_codec == nullptr) {
            continue;
        }

        if (local_codec_parameters->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            codec = (AVCodec*)local_codec;
            codec_parameters = local_codec_parameters;
            break;
        }
    }

    if (video_stream_index == -1) {
        std::cerr << "Could not find a video stream" << std::endl;
        avformat_close_input(&format_context);
        return -1;
    }

    AVCodecContext* codec_context = avcodec_alloc_context3(codec);
    if (!codec_context) {
        std::cerr << "Could not allocate codec context" << std::endl;
        avformat_close_input(&format_context);
        return -1;
    }

    if (avcodec_parameters_to_context(codec_context, codec_parameters) < 0) {
        std::cerr << "Could not copy codec parameters to codec context" << std::endl;
        avcodec_free_context(&codec_context);
        avformat_close_input(&format_context);
        return -1;
    }

    if (avcodec_open2(codec_context, codec, nullptr) < 0) {
        std::cerr << "Could not open codec" << std::endl;
        avcodec_free_context(&codec_context);
        avformat_close_input(&format_context);
        return -1;
    }

    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        std::cerr << "Could not allocate frame" << std::endl;
        avcodec_free_context(&codec_context);
        avformat_close_input(&format_context);
        return -1;
    }

    AVPacket* packet = av_packet_alloc();
    if (!packet) {
        std::cerr << "Could not allocate packet" << std::endl;
        av_frame_free(&frame);
        avcodec_free_context(&codec_context);
        avformat_close_input(&format_context);
        return -1;
    }

    // Read frames from the RTSP stream
    while (av_read_frame(format_context, packet) >= 0) {
        if (packet->stream_index == video_stream_index) {
            int ret = avcodec_send_packet(codec_context, packet);
            if (ret < 0) {
                std::cerr << "Error sending packet for decoding: " << ret/*av_err2str(ret)*/ << std::endl;
                av_packet_unref(packet);
                continue;
            }

            while (ret >= 0) {
                ret = avcodec_receive_frame(codec_context, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    std::cerr << "Error during decoding: " << ret /*av_err2str(ret)*/ << std::endl;
                    break;
                }

                // Print the RTP NTP time for the decoded frame
                print_rtp_statistics(format_context, video_stream_index);

                av_frame_unref(frame);
            }
        }
        av_packet_unref(packet);
    }

    // Cleanup
    av_frame_free(&frame);
    av_packet_free(&packet);
    avcodec_free_context(&codec_context);
    avformat_close_input(&format_context);

    avformat_network_deinit();

    return 0;
}
